﻿using Cjj.Exam.Dtos;
using Cjj.Exam.Entities;
using Cjj.Exam.IRepositorys;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using System.Linq.Dynamic.Core;
using Microsoft.AspNetCore.Authorization;
using Cjj.Exam.Permissions;
using Abp.Authorization;
using Volo.Abp.Authorization.Permissions;

namespace Cjj.Exam
{
    public class CjjBookAppService : CrudAppService<CjjBook, CjjBookDto, Guid>
    {
        private readonly IAuthorRepository _authorRepository;
        public CjjBookAppService(IRepository<CjjBook, Guid> repository, IAuthorRepository authorRepository, IPermissionManager permissionManager, IPermissionStore permissionStore) : base(repository)
        {
            _authorRepository = authorRepository;

            GetPolicyName = ExamPermissions.CjjBook.Default;
            GetListPolicyName = ExamPermissions.CjjBook.Default;
            CreatePolicyName = ExamPermissions.CjjBook.Create;
            UpdatePolicyName = ExamPermissions.CjjBook.Edit;
            DeletePolicyName = ExamPermissions.CjjBook.Delete;

            _permissionManager = permissionManager;
        }
        private readonly IPermissionManager _permissionManager;

        //public async Task GrantRolePermissionDemoAsync(string roleName, string permission)
        //{
        //    await _permissionManager.SetForRoleAsync(roleName, permission, true);
        //}

        //public async Task GrantUserPermissionDemoAsync(
        //    Guid userId, string roleName, string permission)
        //{
        //    await _permissionManager.SetForUserAsync(userId, permission, true);
        //}



        public override async Task<CjjBookDto> GetAsync(Guid id)
        {
            await AuthorizationService.CheckAsync("Author_Management_Create_Books");

            //Get the IQueryable<Book> from the repository
            var queryable = await Repository.GetQueryableAsync();

            //Prepare a query to join books and authors
            var query = from book in queryable
                        join author in await _authorRepository.GetQueryableAsync() on book.AuthorId equals author.Id
                        where book.Id == id
                        select new { book, author };

            //Execute the query and get the book with author
            var queryResult = await AsyncExecuter.FirstOrDefaultAsync(query);
            if (queryResult == null)
            {
                throw new EntityNotFoundException(typeof(CjjBook), id);
            }

            var bookDto = ObjectMapper.Map<CjjBook, CjjBookDto>(queryResult.book);
            bookDto.AuthorName = queryResult.author.Name;
            return bookDto;
        }


        [Authorize(ExamPermissions.CjjBook.Default)]
        public override async Task<PagedResultDto<CjjBookDto>> GetListAsync(PagedAndSortedResultRequestDto input)
        {
            //Get the IQueryable<Book> from the repository
            var queryable = await Repository.GetQueryableAsync();

            //Prepare a query to join books and authors
            var query = from book in queryable
                        join author in await _authorRepository.GetQueryableAsync() on book.AuthorId equals author.Id
                        select new { book, author };

            //Paging
            query = query
                .OrderBy(NormalizeSorting(input.Sorting))
                .Skip(input.SkipCount)
                .Take(input.MaxResultCount);

            //Execute the query and get a list
            var queryResult = await AsyncExecuter.ToListAsync(query);

            //Convert the query result to a list of BookDto objects
            var bookDtos = queryResult.Select(x =>
            {
                var bookDto = ObjectMapper.Map<CjjBook, CjjBookDto>(x.book);
                bookDto.AuthorName = x.author.Name;
                return bookDto;
            }).ToList();

            //Get the total count with another query
            var totalCount = await Repository.GetCountAsync();

            return new PagedResultDto<CjjBookDto>(
                totalCount,
                bookDtos
            );
        }

        public async Task<ListResultDto<AuthorLookupDto>> GetAuthorLookupAsync()
        {
            var authors = await _authorRepository.GetListAsync();

            return new ListResultDto<AuthorLookupDto>(
                ObjectMapper.Map<List<Author>, List<AuthorLookupDto>>(authors)
            );
        }

        private static string NormalizeSorting(string sorting)
        {
            if (sorting.IsNullOrEmpty())
            {
                return $"book.{nameof(CjjBook.Name)}";
            }

            if (sorting.Contains("authorName", StringComparison.OrdinalIgnoreCase))
            {
                return sorting.Replace(
                    "authorName",
                    "author.Name",
                    StringComparison.OrdinalIgnoreCase
                );
            }

            return $"book.{sorting}";
        }
    }
}
